﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Emu8080.Compiler.Structures
{
    public class Instruction : ProcessingUnit, IProcessingUnitFundamental
    {
        public UInt16 Address { get; set; }
        public Operators Operator { get; set; }

        public virtual int Size
        {
            get
            {
                return 1;
            }
        }

        public virtual byte[] GetMachineCodes()
        {
            return new byte[] { (byte)Operator };
        }

        public override string ToString()
        {
            string codes = "";
            foreach (var item in GetMachineCodes())
            {
                codes += item.ToString("x2") + " ";
            }

            return string.Format("{0:x4}: {1} {2}", Address, codes, Operator);
        }
    }

    // code = [Operator, Operand_lowbyte, Operand_highbyte]
    class InstructionIm16 : Instruction
    {
        public ImmediateOperand16bit Operand { get; set; }

        public override int Size
        {
            get
            {
                return 3;
            }
        }

        public override byte[] GetMachineCodes()
        {
            ushort operand = 0;
            if (Operand.Type == ImmediateOperand16bit.ImmediateOperand16bitType.Number)
                operand = (ushort)(Int16)Operand.Value;
            return new byte[] { (byte)Operator, (byte)(operand & 0xFF), (byte)(operand >> 8) };
        }
    }

    // code = [Operator | (Operand1 << 4), Operand2_lowbyte, Operand2_highbyte]
    class InstructionRpIm16 : Instruction
    {
        public RegisterPairs Operand1 { get; set; }
        public ImmediateOperand16bit Operand2 { get; set; }

        public override int Size
        {
            get
            {
                return 3;
            }
        }

        public override byte[] GetMachineCodes()
        {
            ushort operand = 0;
            if (Operand2.Type == ImmediateOperand16bit.ImmediateOperand16bitType.Number)
                operand = (ushort)(Int16)Operand2.Value;
            return new byte[] { (byte)((byte)Operator | ((int)Operand1 << 4)), (byte)(operand & 0xFF), (byte)(operand >> 8) };
        }
    }

    // code = [Operator, Operand]
    class InstructionIm8 : Instruction
    {
        public Byte Operand { get; set; }

        public override int Size
        {
            get
            {
                return 2;
            }
        }

        public override byte[] GetMachineCodes()
        {
            return new byte[] { (byte)Operator, Operand };
        }
    }


    // code = [Operator | (Operand1 << 3), Operand2]
    class InstructionRegIm8 : Instruction
    {
        public Registers Operand1 { get; set; }
        public Byte Operand2 { get; set; }

        public override int Size
        {
            get
            {
                return 2;
            }
        }

        public override byte[] GetMachineCodes()
        {
            return new byte[] { (byte)((byte)Operator | ((int)Operand1 << 3)), Operand2 };
        }
    }

    // code = [Operator | (Operand1 << 3) | Operand2]
    class InstructionRegReg : Instruction
    {
        public Registers Operand1 { get; set; }
        public Registers Operand2 { get; set; }

        public override byte[] GetMachineCodes()
        {
            return new byte[] { (byte)((byte)Operator | ((int)Operand1 << 3) | (int)Operand2) };
        }
    }

    // code = [Operator | Operand]
    class InstructionReg : Instruction
    {
        public Registers Operand { get; set; }

        public override byte[] GetMachineCodes()
        {
            return new byte[] { (byte)((byte)Operator | (int)Operand) };
        }
    }

    // code = [Operator | Operand << 4]
    class InstructionRp : Instruction
    {
        public RegisterPairs Operand { get; set; }

        public override byte[] GetMachineCodes()
        {
            return new byte[] { (byte)((byte)Operator | ((int)Operand << 4)) };
        }
    }

    // especial instruction
    // code = [Operator | Operand << 3]
    class InstructionRst : Instruction
    {
        public int Operand { get; set; }

        public override byte[] GetMachineCodes()
        {
            return new byte[] { (byte)((byte)Operator | ((int)Operand << 3)) };
        }
    }
}
